<script setup lang="ts">
import { nextTick, reactive, ref, watch } from 'vue';

import { VIcon } from '@croco/framework/icons'

import { onClickOutside } from '@vueuse/core';

// eslint-disable-next-line import/no-self-import
import SubMenu from './index.vue';

export interface IMenuItemProps {
    id?: string;
    label?: string;
    icon?: string;
    hidden?: boolean;
    disabled?: boolean;
    separator?: boolean;
    children?: IMenuItemProps[];
    shortcut?: string;
    onClick?: (data: { data: Array<any>; menu: IMenuItemProps }) => boolean | void;
}

interface IContextMenuProps {
    data: ContextmenuDataProps;
    visible?: boolean;
    width?: number;
    beforeOpen?: (parameter: any, menus: Array<IMenuItemProps>) => boolean;
}

type PasteType = 'copy' | 'move';
export interface ContextmenuProps {
    data: ContextmenuDataProps;
    paste?: PasteProps;
    menus: Array<IMenuItemProps>;
    onBeforeOpen: (parameter: any, menus: Array<any>) => boolean;
    onOpen: (event: MouseEvent, row?: any) => void;
    onClick?: (menu: any, data: any[]) => void;
}

export interface ContextmenuDataProps {
    position: number[];
    parameter: any;
}

export interface PasteProps {
    type: PasteType;
    path: string;
    data: Array<any>;
}

const props = withDefaults(defineProps<IContextMenuProps>(), {
    visible: false,
    width: 290,
    beforeOpen: () => true,
});

const emit = defineEmits(['click']);

const menus = defineModel<Array<IMenuItemProps>>({ default: [] });

const visible = ref(props.visible);
const contextMenuRef = ref();

// 计算菜单高度
const calculateSubmenuHeight = (menus: Array<IMenuItemProps>) => {
    const spCount = menus.filter((item) => item.separator).length;
    const muCount = menus.filter((item) => item.label && !item.hidden).length;
    // 8 * 2: padding 8; 18: svg:height 18px
    return spCount * 9 + muCount * (8 * 2 + 18) + 20;
};

const height = calculateSubmenuHeight(menus.value); // 菜单高度（假设每个菜单项高度为 30px;头尾 padding 10则共计20）

// 弹出菜单
const pos = reactive({ x: props.data.position[0], y: props.data.position[1] });
watch(
    () => props.data.position,
    (p) => {
        active.value = null;
        const status = props.beforeOpen(props.data.parameter, menus.value);
        pos.x = (p[0] ?? 0) + props.width > window.innerWidth ? window.innerWidth - props.width : p[0];
        pos.y = (p[1] ?? 0) + height > window.innerHeight ? window.innerHeight - height : p[1];
        nextTick(() => (visible.value = status));
    },
);

// 弹出子菜单
const active = ref<null | number>(null);
const submenuData = reactive<ContextmenuDataProps>({
    position: [0, 0],
    parameter: props.data.parameter,
});
const onMouseEnter = (item: IMenuItemProps, index: number, event: any) => {
    if (item.disabled) return;

    active.value = item.children ? index : null;

    if (item.children && contextMenuRef.value) {
        if (props.beforeOpen(submenuData.parameter, item.children)) {
            const rect = event.target.getBoundingClientRect();

            // 计算子菜单位置
            submenuData.position[0] = rect.right + 1;
            submenuData.position[1] = rect.top;

            // 检查右侧空间
            if ((submenuData.position[0] ?? 0) + props.width > window.innerWidth) {
                submenuData.position[0] = rect.left - props.width - 1;
            }

            // 检查底部空间
            const submenuHeight = calculateSubmenuHeight(item.children);
            if ((submenuData.position[1] ?? 0) + submenuHeight > window.innerHeight) {
                submenuData.position[1] = window.innerHeight - submenuHeight;
            }
        } else {
            active.value = null;
        }
    }
};

const hide = () => {
    active.value = null;
    visible.value = false;
};

const onMenuItemClick = (item: IMenuItemProps) => {
    if (!item) {
        emit('click');
        hide();
        return;
    }

    if (item.disabled || item.children) return;

    if (typeof item.onClick === 'function') {
        item.onClick({ data: props.data.parameter, menu: item });
        emit('click');
        hide();
    } else {
        emit('click', { data: props.data.parameter, menu: item });
        hide();
    }
};
// 点击菜单之外的任一区域都关闭菜单
onClickOutside(contextMenuRef, () => hide(), { ignore: [] });
// 暴露方法给父组件
defineExpose({
    hide,
});
</script>

<template>
    <div
        v-show="visible"
        ref="contextMenuRef"
        :style="{ '--context-menu-top': `${pos.y}px`, '--context-menu-left': `${pos.x}px`, '--context-menu-width': `${width}px` }"
        class="right-click-menu"
        @click.stop
    >
        <ul>
            <template v-for="(item, index) in menus" :key="index">
                <li
                    v-if="!item.hidden"
                    class="contextmenu-item"
                    :class="{ disabled: item.disabled, separator: item.separator, 'has-children': item.children, active: active === index }"
                    @click.stop.prevent="onMenuItemClick(item)"
                    @mouseenter="onMouseEnter(item, index, $event)"
                >
                    <span class="icon">
                        <VIcon :icon="item.icon" />
                    </span>
                    <span class="label">{{ item.label }}</span>
                    <span v-if="item.shortcut" class="shortcut">
                        {{ item.shortcut }}
                    </span>
                    <span v-if="item.children" class="arrow">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#50153b" d="M8 5.14v14l11-7z"/></svg>
                    </span>
                </li>
                <SubMenu
                    v-if="item.children && active === index"
                    v-model="item.children"
                    :data="submenuData"
                    :width="width"
                    :visible="true"
                    :before-open="beforeOpen"
                    @click="onMenuItemClick"
                />
            </template>
        </ul>
    </div>
</template>

<style lang="scss" scoped>
.right-click-menu {
    font-family: serif;
    font-size: 12px;
    color: var(--el-text-color-regular);
    position: fixed;
    background-color: var(--el-bg-color);
    border: 1px solid var(--el-border-color);
    box-shadow: 0 2px 10px var(--el-border-color);
    border-radius: 10px;
    z-index: 1000;
    top: var(--context-menu-top);
    left: var(--context-menu-left);
    width: var(--context-menu-width);
    padding: 10px 0;

    ul {
        list-style: none;
        margin: 0;
        padding: 0;
    }

    .contextmenu-item {
        display: flex;
        align-items: center;
        padding: 8px 16px;
        cursor: default;
        position: relative;
    }

    .contextmenu-item.active,
    .contextmenu-item:hover {
        background-color: var(--el-fill-color-light);
    }

    .contextmenu-item.disabled {
        color: #999;
        cursor: not-allowed;
    }

    .contextmenu-item.separator {
        height: 1px;
        background-color: var(--el-border-color-lighter);
        margin: 4px 0;
        padding: 0;
        cursor: default;
    }

    .icon {
        margin-right: 8px;
    }

    .icon,
    svg {
        display: flex;
        height: 18px;
        width: 18px;
    }

    .label {
        flex: 1;
        display: flex;
    }

    .shortcut {
        margin-left: 16px;
        color: var(--el-text-color-regular);
    }

    .arrow {
        margin-left: 8px;
        font-size: 12px;
        color: var(--el-text-color-regular);
    }
}

.right-click-menu .has-children {
    padding-right: 24px;
}
</style>
